/*
 * ApplicationMIDlet.java
 *
 * Created on 2008/06/07 +0900
 *
 * $Revision: 42 $
 * $Date: 2008-10-26 14:12:53 +0000 (Sun, 26 Oct 2008) $
 */

package org.gikolet.framework.platform.midp;

import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import org.gikolet.framework.Toolkit;
import org.gikolet.framework.application.Application;
import org.gikolet.framework.application.ApplicationLifecycle;
import org.gikolet.framework.application.ApplicationRequestProcesser;
import org.gikolet.framework.inject.Binder;
import org.gikolet.framework.inject.DefaultInjector;
import org.gikolet.framework.inject.Injector;
import org.gikolet.framework.inject.InstanceIterator;
import org.gikolet.framework.inject.Moduleable;
import org.gikolet.framework.inject.Provider;
import org.gikolet.framework.inject.Scope;
import org.gikolet.framework.inject.Serviceable;
import org.gikolet.framework.inject.SingletonClassProvider;
import org.gikolet.framework.io.http.HttpConnectionProvider;
import org.gikolet.framework.io.storage.Scratchpad;
import org.gikolet.framework.platform.PlatformDependentFunction;
import org.gikolet.framework.platform.midp.internal.MIDPHttpConnectionProvider;
import org.gikolet.framework.platform.midp.internal.MIDPDependentFunction;
import org.gikolet.framework.platform.midp.internal.MIDPDisplay;
import org.gikolet.framework.platform.midp.internal.MIDPScratchpad;
import org.gikolet.framework.request.Request;
import org.gikolet.framework.ui.Display;
import org.gikolet.framework.util.Assert;

/**
 * @author Tetsutaro (latest modification by $Author: tetsutaro.ss $)
 * @version $Revision: 42 $ $Date: 2008-10-26 14:12:53 +0000 (Sun, 26 Oct 2008) $
 */
public class ApplicationMIDlet extends MIDlet implements Application, Moduleable {
    private ApplicationRequestProcesser requestProcesser;
    private Injector injector;
    private boolean initialized = false;

    public ApplicationMIDlet() {
	this.requestProcesser = new ApplicationRequestProcesser();
	this.requestProcesser.startRequest();
	try {
	    this.requestProcesser.invokeAndWait(new Request() {
		public void execute() {
		    new DefaultInjector(new Moduleable[] { ApplicationMIDlet.this });
		}
	    });
	} catch (Throwable e) {
	    e.printStackTrace();
	}
    }

    public void configure(Binder binder) {
	Provider displayProvider = new SingletonClassProvider(MIDPDisplay.class);

	binder.bind(Serviceable.class).toInstance(new Serviceable() {
	    public void service(Injector injector) {
		ApplicationMIDlet.this.service(injector);
	    }
	});
	binder.bind(ApplicationRequestProcesser.class).toInstance(this.requestProcesser);
	binder.bind(MIDlet.class).toInstance(this);
	binder.bind(Application.class).toInstance(this);
	binder.bind(Display.class).toProvider(displayProvider);
	binder.bind(Serviceable.class).toProvider(displayProvider);
	binder.bind(ApplicationLifecycle.class).toProvider(displayProvider);
	binder.bind(PlatformDependentFunction.class).to(MIDPDependentFunction.class).in(
		Scope.SINGLETON);
	binder.bind(Scratchpad.class).to(MIDPScratchpad.class).in(Scope.SINGLETON);
	binder.bind(HttpConnectionProvider.class).to(MIDPHttpConnectionProvider.class)
		.in(Scope.SINGLETON);
    }

    private void service(Injector injector) {
	this.injector = injector;
	Toolkit.service(injector);
    }

    protected final void startApp() throws MIDletStateChangeException {
	Assert.isNotNull(this.injector);

	synchronized (this.requestProcesser) {
	    if (!this.requestProcesser.isAlive()) {
		this.requestProcesser.startRequest();
	    }
	}
	try {
	    this.requestProcesser.invokeAndWait(new Request() {
		public void execute() {
		    if (!ApplicationMIDlet.this.initialized) {
			System.out.println("Application initialize.");
			InstanceIterator iterator = ApplicationMIDlet.this.injector
				.getInstanceIterator(ApplicationLifecycle.class);
			while (iterator.hasNext()) {
			    ((ApplicationLifecycle) iterator.next()).initialize();
			}
			ApplicationMIDlet.this.initialized = true;
		    }

		    System.out.println("Application start.");
		    InstanceIterator iterator = ApplicationMIDlet.this.injector
			    .getInstanceIterator(ApplicationLifecycle.class);
		    while (iterator.hasNext()) {
			((ApplicationLifecycle) iterator.next()).start();
		    }
		}
	    });
	} catch (InterruptedException e) {
	} catch (Throwable e) {
	    e.printStackTrace();
	}
    }

    protected final void pauseApp() {
	Assert.isNotNull(this.injector);

	try {
	    this.requestProcesser.invokeAndWait(new Request() {
		public void execute() {
		    System.out.println("Application pause.");

		    InstanceIterator iterator = ApplicationMIDlet.this.injector
			    .getInstanceIterator(ApplicationLifecycle.class);

		    while (iterator.hasNext()) {
			((ApplicationLifecycle) iterator.next()).pause();
		    }
		}
	    });
	} catch (InterruptedException e) {
	} catch (Throwable e) {
	    e.printStackTrace();
	}
    }

    protected final void destroyApp(final boolean b) {
	Assert.isNotNull(this.injector);

	if (!this.requestProcesser.isRequestProcesserThread()) {
	    try {
		this.requestProcesser.invokeAndWait(new Request() {
		    public void execute() {
			destroyApp(b);
		    }
		});
	    } catch (InterruptedException e) {
	    } catch (Throwable e) {
		e.printStackTrace();
	    }
	} else {
	    System.out.println("Application destroy.");
	    InstanceIterator iterator = ApplicationMIDlet.this.injector
		    .getInstanceIterator(ApplicationLifecycle.class);

	    while (iterator.hasNext()) {
		((ApplicationLifecycle) iterator.next()).destroy();
	    }

	    synchronized (this.requestProcesser) {
		if (this.requestProcesser.isAlive()) {
		    this.requestProcesser.stopAllRequest();
		}
	    }
	}
    }

    public final void notifyApplicationDestroyed() {
	destroyApp(false);
	notifyDestroyed();
    }

    public final void notifyApplicationPaused() {
	pauseApp();
	notifyPaused();
    }
}
